using System;
using mshtml;
using System.Diagnostics;
using Microsoft.Win32;
using System.Threading;
using System.Runtime.InteropServices;

using PasswordProvider.ViewModel.Properties;
using System.Globalization;

namespace PasswordProvider.ViewModel.Helpers
{
    internal static class IENavigationHelper
    {
        #region Public Methods

        // Determine if Internet Explorer is the default browser. Works with Vista and Windows 7, not XP...
        public static bool IsIETheDefaultBrowser()
        {
            ApplicationAssociationRegistration appReg = new ApplicationAssociationRegistration();
            string defaultBrowser = (appReg as IApplicationAssociationRegistration).QueryCurrentDefault(
                "http", AssociationType.UrlProtocol, AssociationLevel.Effective);
            
            if (defaultBrowser != null)
                return defaultBrowser.ToUpperInvariant().Contains("IEXPLORE");

            // TODO: Determine default browser in XP.
            return false;
        }

        /// <summary>
        /// If Internet Explorer is not currently running, then it is openned at the url specified.
        /// If IE is currently running, then the url is openned in a new tab or, if the url is already 
        /// openned, the window that contains it is returned.
        /// </summary>
        public static HTMLDocument OpenUrl(Uri uri)
        {
            string url = uri.AbsoluteUri;
    
            // Open the window.
            SHDocVw.InternetExplorer ie = FindIEWindowOpenAtURL(url);
            if (ie == null)
                ie = NavigateToUrl(url);
            
            if (ie != null)
                return ie.Document as HTMLDocument;
            else
                return null;
        }

        #endregion

        #region Private Helper Methods

        // Find an open window (or tab) in IE currently pointed at the indicated URL, if there is one. 
        private static SHDocVw.InternetExplorer FindIEWindowOpenAtURL(string url)
        {
            SHDocVw.ShellWindows windows = new SHDocVw.ShellWindowsClass();
            foreach (SHDocVw.InternetExplorer window in windows)
            {
                if (window.FullName.IndexOf("iexplore.exe", StringComparison.OrdinalIgnoreCase) > 0)
                {
                    if (AreSameLocation(url, window.LocationURL))
                        return window;
                }
            }

            return null;
        }

        // Determine if two urls are the same domain.
        private static bool AreSameLocation(string url1, string url2)
        {
            Uri uri1 = new Uri(url1);
            Uri uri2 = new Uri(url2);
            return uri1.DnsSafeHost.Equals(uri2.DnsSafeHost, StringComparison.OrdinalIgnoreCase);
        }

        // Navigate to the url in internet explorer using a new tab, if IE is currently running,
        // or start a new instance of IE. Waits for the page to finish loading before returning.
        private static SHDocVw.InternetExplorer NavigateToUrl(string url)
        {
            SHDocVw.InternetExplorer existingWindow = null;

            // Obtain existing ie window if there is one.
            SHDocVw.ShellWindows windows = new SHDocVw.ShellWindowsClass();
            foreach (SHDocVw.InternetExplorer window in windows)
            {
                if (window.FullName.IndexOf("iexplore.exe", StringComparison.OrdinalIgnoreCase) > 0)
                {
                    existingWindow = window;
                    break;
                }
            }
            // Create a wait handle to be triggered when the new tab is openned.
            ManualResetEvent waitForNewWindow = new ManualResetEvent(false);

            SHDocVw.InternetExplorer newTabWindow = null;

            // Register an event handler for the new window event. This event is raised
            // by Internet Explorer when a new window is registered, this includes when
            // a tab is added to an existing instance of the application.
            windows.WindowRegistered +=
                delegate
                {
                    foreach (SHDocVw.InternetExplorer window in windows)
                    {
                        // If there is an existing IE Window
                        if (existingWindow != null)
                        {
                            // Looking for a new window that has the same parent as the existing window we call Navigate from.
                            if (window.HWND == existingWindow.HWND)
                            {
                                // If this is an empty url, then it is probably the one wanted,
                                // because it appears that this event is always fired before the window 
                                // actually knows where it is going (what url)
                                string currentUrl = window.LocationURL;
                                if (String.IsNullOrEmpty(currentUrl))
                                {
                                    newTabWindow = window;
                                    waitForNewWindow.Set();
                                    break;
                                }
                            }
                        }
                        else // No existing IE, looking for new IE window.
                        {
                            if (window.FullName.IndexOf("iexplore.exe", StringComparison.OrdinalIgnoreCase) > 0)
                            {
                                newTabWindow = existingWindow = window;
                                waitForNewWindow.Set();
                                break;
                            }
                        }
                    }
                };

            if (existingWindow != null)
            {
                // Perform navigation into new tab.
                object nullPointer = null;
                object flag = 0x0800; // navOpenInNewTab
                existingWindow.Navigate(url, ref flag, ref nullPointer, ref nullPointer, ref nullPointer);
            }
            else
            {
                // if no existing window, create to a new one.          
                Process.Start("IExplore", url);
            }

            // wait for the ie window to load.
            waitForNewWindow.WaitOne(10000, false);

            // newTabWindow is set in the event handler delegate if all goes well.
            if (newTabWindow != null)
            {
                try
                {
                    // wait for the new window to stop being busy before returning.
                    while (newTabWindow != null && newTabWindow.Busy)
                        Thread.Sleep(100);
                }
                catch (InvalidCastException)
                { // User closed the tab right after it was opened
                    return null;
                }
                catch (COMException)
                { // User closed IE before page loaded.
                    return null;
                }
            }

            return newTabWindow;
        }

        #endregion // Private helper methods
    }
}
